package com.dlc.shop.service.impl;

import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dlc.shop.bean.app.dto.SkuDto;
import com.dlc.shop.bean.app.vo.ProductVO;
import com.dlc.shop.bean.app.vo.SkuVO;
import com.dlc.shop.bean.dto.*;
import com.dlc.shop.bean.enums.StockBillStatus;
import com.dlc.shop.bean.enums.StockBillType;
import com.dlc.shop.bean.enums.StockType;
import com.dlc.shop.bean.model.*;
import com.dlc.shop.bean.vo.*;
import com.dlc.shop.common.config.Constant;
import com.dlc.shop.common.constants.AllotOrderStatusEnum;
import com.dlc.shop.common.constants.LuaOperateEnum;
import com.dlc.shop.common.constants.SegmentIdKey;
import com.dlc.shop.common.constants.StockPointType;
import com.dlc.shop.common.enums.SysTypeEnum;
import com.dlc.shop.common.exception.YamiShopBindException;
import com.dlc.shop.common.i18n.I18nMessage;
import com.dlc.shop.common.i18n.LanguageEnum;
import com.dlc.shop.common.util.PageParam;
import com.dlc.shop.common.util.PoiExcelUtil;
import com.dlc.shop.dao.AllotOrderMapper;
import com.dlc.shop.dao.StationMapper;
import com.dlc.shop.service.*;
import com.dlc.shop.util.StockUtil;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author gaozijie
 * @since 2023-11-20
 */
@Service
@AllArgsConstructor
public class AllotOrderServiceImpl extends ServiceImpl<AllotOrderMapper, AllotOrder> implements AllotOrderService {

    private static final Logger log = LoggerFactory.getLogger(AllotOrderServiceImpl.class);

    private final AllotOrderMapper allotOrderMapper;
    private final StationMapper stationMapper;
    private final AllotOrderItemService allotOrderItemService;
    private final DeliveryService deliveryService;
    private final ProductService productService;
    private final SegmentService segmentService;
    private final SkuService skuService;
    private final WarehouseService warehouseService;
    private final StockBillLogService stockBillLogService;
    private final SkuStockService skuStockService;
    private final StockPointSkuService stockPointSkuService;
    private final SkuStockLockService skuStockLockService;

    @Override
    public Page<AllotOrderVO> pageAllotOrder(PageParam<AllotOrderVO> pageParam, AllotOrderDTO allotOrderDTO) {
        Page<AllotOrderVO> page = allotOrderMapper.pageAllotOrder(pageParam, allotOrderDTO);
        if (CollectionUtils.isEmpty(page.getRecords())) {
            return page;
        }
        // 补充仓库名称
        Set<Long> warehouseIds = new HashSet<>(Constant.INITIAL_CAPACITY);
        for (AllotOrderVO allotOrderVO : page.getRecords()) {
            warehouseIds.add(allotOrderVO.getOutWarehouseId());
            warehouseIds.add(allotOrderVO.getInWarehouseId());
        }
        WarehouseDTO warehouseDTO = new WarehouseDTO();
        warehouseDTO.setWarehouseIds(new ArrayList<>(warehouseIds));
        List<WarehouseVO> warehouses = warehouseService.listWarehouse(warehouseDTO);
        if (!CollectionUtils.isEmpty(warehouses)) {
            Map<Long, String> warehouseMap = warehouses.stream().collect(Collectors.toMap(WarehouseVO::getWarehouseId, WarehouseVO::getWarehouseName));
            for (AllotOrderVO allotOrderVO : page.getRecords()) {
                allotOrderVO.setOutStockPointName(warehouseMap.get(allotOrderVO.getOutWarehouseId()));
                allotOrderVO.setInStockPointName(warehouseMap.get(allotOrderVO.getInWarehouseId()));
            }
        }
        return page;
    }

    @Override
    public List<AllotOrderVO> listAllotOrder(AllotOrderDTO allotOrderDTO) {
        return allotOrderMapper.listAllotOrder(allotOrderDTO);
    }

    @Override
    public AllotOrderVO getAllotOrder(AllotOrderDTO allotOrderDTO) {
        AllotOrderVO allotOrderVO = allotOrderMapper.getAllotOrder(allotOrderDTO);
        // 补充物流公司名称
        Delivery deliveryCompany = deliveryService.getInfoById(allotOrderDTO.getDvyCompanyId());
        if (!Objects.isNull(deliveryCompany)) {
            allotOrderVO.setDvyCompanyName(deliveryCompany.getDvyName());
        }
        // 补充仓库名称
        WarehouseDTO warehouseDTO = new WarehouseDTO();
        warehouseDTO.setWarehouseIds(Arrays.asList(allotOrderVO.getInWarehouseId(), allotOrderVO.getOutWarehouseId()));
        List<WarehouseVO> warehouses = warehouseService.listWarehouse(warehouseDTO);
        if (!CollectionUtils.isEmpty(warehouses)) {
            Map<Long, String> warehouseMap = warehouses.stream().collect(Collectors.toMap(WarehouseVO::getWarehouseId, WarehouseVO::getWarehouseName));
            allotOrderVO.setOutStockPointName(warehouseMap.get(allotOrderVO.getOutWarehouseId()));
            allotOrderVO.setInStockPointName(warehouseMap.get(allotOrderVO.getInWarehouseId()));
        }
        // 补充商品名称
        this.fillAllotItemInfo(allotOrderVO.getAllotOrderItemVOList());
        return allotOrderVO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addAllotOrder(AllotOrderDTO allotOrderDTO) {
        this.checkParam(allotOrderDTO);
        AllotOrder allotOrder = this.buildAllotOrder(allotOrderDTO);
        allotOrderDTO.setAllotOrderId(allotOrder.getAllotOrderId());
        // 库存锁定
        List<SkuStockVO> skuStockList = allotOrderDTO.getAllotOrderItemDTOList().stream()
                .map(x -> new SkuStockVO(allotOrder.getAllotOrderId().toString(), x.getSkuId(), x.getAllotCount(), allotOrderDTO.getOutWarehouseId())).toList();
        this.lockStock(skuStockList);
        // 新增调拨订单
        this.save(allotOrder);
        // 新增调拨订单商品
        List<Long> skuIds = new ArrayList<>(allotOrderDTO.getAllotOrderItemDTOList().size());
        for (AllotOrderItemDTO allotOrderItemDTO : allotOrderDTO.getAllotOrderItemDTOList()) {
            allotOrderItemDTO.setAllotOrderId(allotOrder.getAllotOrderId());
            allotOrderItemDTO.setShopId(allotOrderDTO.getShopId());
            allotOrderItemDTO.setInboundCount(allotOrderItemDTO.getAllotCount());
            skuIds.add(allotOrderItemDTO.getSkuId());
        }
        allotOrderItemService.addBatchAllotOrderItem(allotOrderDTO.getAllotOrderItemDTOList());
        // 解锁库存（调拨订单不存在支付，创建成功后直接解锁）
        skuStockLockService.reduceLockAllotOrder(skuStockList);
        // 更新仓库和商品关联
        this.updateStockPointSkuRelate(Collections.singletonList(allotOrderDTO.getOutWarehouseId()), skuIds);
        // 新增出库库存流水
        StockBillLog stockBillLog = this.buildStockBillLog(allotOrderDTO, allotOrder.getOutWarehouseId(), allotOrder.getOutStockPointType(), StockBillType.ALLOT_OUT);
        stockBillLogService.addStockBillLog(stockBillLog);
    }

    @Override
    public void updateAllotOrder(AllotOrderDTO allotOrderDTO) {
        if (Objects.isNull(allotOrderDTO)) {
            return;
        }
        AllotOrder allotOrder = new AllotOrder();
        BeanUtils.copyProperties(allotOrderDTO, allotOrder);
        allotOrder.setUpdateTime(LocalDateTime.now());
        this.updateById(allotOrder);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void inbound(AllotOrderDTO allotOrderDTO) {
        AllotOrderVO dbAllotOrder = this.checkOrderStatus(allotOrderDTO);
        // 过滤掉入库数量为0的数据
        List<AllotOrderItemDTO> allotOrderItems = allotOrderDTO.getAllotOrderItemDTOList().stream().filter(x -> x.getInboundCount() != null && x.getInboundCount() > 0).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(allotOrderItems)) {
            throw new YamiShopBindException("yami.allot.inbound.zero");
        }
        allotOrderDTO.setAllotOrderItemDTOList(allotOrderItems);
        allotOrderDTO.setOutStockPointType(dbAllotOrder.getOutStockPointType());
        allotOrderDTO.setInStockPointType(dbAllotOrder.getInStockPointType());
        // 修改调拨商品入库数量
        Map<Long, AllotOrderItemVO> dbItemMap = dbAllotOrder.getAllotOrderItemVOList().stream().collect(Collectors.toMap(AllotOrderItemVO::getSkuId, x -> x));
        List<AllotOrderItemDTO> updateItems = new ArrayList<>(Constant.INITIAL_CAPACITY);
        AllotOrderItemDTO updateItem;
        AllotOrderItemVO dbItem;
        for (AllotOrderItemDTO inboundItem : allotOrderItems) {
            dbItem = dbItemMap.get(inboundItem.getSkuId());
            if (Objects.isNull(dbItem)) {
                throw new YamiShopBindException("yami.allot.inbound.item.error");
            }
            if (inboundItem.getInboundCount() > dbItem.getAllotCount() - dbItem.getAllotInCount()) {
                throw new YamiShopBindException("yami.allot.inbound.out");
            }
            dbItem.setAllotInCount(dbItem.getAllotInCount() + inboundItem.getInboundCount());
            updateItem = new AllotOrderItemDTO();
            updateItem.setAllotOrderId(dbAllotOrder.getAllotOrderId());
            updateItem.setAllotOrderItemId(dbItem.getAllotOrderItemId());
            updateItem.setAllotInCount(dbItem.getAllotInCount());
            updateItems.add(updateItem);
        }
        allotOrderItemService.updateBatchAllotOrderItem(updateItems);
        // 修改调拨订单状态
        boolean isCompete = true;
        for (AllotOrderItemVO item : dbAllotOrder.getAllotOrderItemVOList()) {
            isCompete = isCompete && (Objects.equals(item.getAllotCount(), item.getAllotInCount()));
        }
        AllotOrderDTO updateOrder = new AllotOrderDTO();
        updateOrder.setAllotOrderId(allotOrderDTO.getAllotOrderId());
        updateOrder.setRemark(allotOrderDTO.getRemark());
        updateOrder.setStatus(isCompete ? AllotOrderStatusEnum.COMPLETION.getNum() : AllotOrderStatusEnum.PARTIALLY_STOCK.getNum());
        this.updateAllotOrder(updateOrder);
        // 修改库存(入库仓增加库存)
        List<SkuStockVO> skuStockVOList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        for (AllotOrderItemDTO allotOrderItemDTO : allotOrderItems) {
            skuStockVOList.add(this.buildSkuStockVO(allotOrderItemDTO, dbAllotOrder.getInWarehouseId(), dbAllotOrder.getInStockPointType(), LuaOperateEnum.SKU_ADD));
        }
        skuStockService.changeSkuStock(skuStockVOList);
        // 新增入库库存流水
        StockBillLog stockBillLog = this.buildStockBillLog(allotOrderDTO, allotOrderDTO.getInWarehouseId(), allotOrderDTO.getInStockPointType(), StockBillType.ALLOT_IN);
        stockBillLogService.addStockBillLog(stockBillLog);
    }

    @Override
    public void completeOrder(AllotOrderDTO allotOrderDTO) {
        this.checkOrderStatus(allotOrderDTO);
        allotOrderDTO.setStatus(AllotOrderStatusEnum.COMPLETION.getNum());
        this.updateAllotOrder(allotOrderDTO);
    }

    @Override
    public void nullifyOrder(AllotOrderDTO allotOrderDTO) {
        AllotOrderVO dbAllotOrder = this.checkOrderStatus(allotOrderDTO);
        // 过滤出需要退还库存的数据，并转成dto
        List<AllotOrderItemDTO> allotOrderItems = dbAllotOrder.getAllotOrderItemVOList().stream()
                .filter(x -> x.getAllotCount() > x.getAllotInCount())
                .map(x -> {
                    AllotOrderItemDTO allotOrderItemDTO = new AllotOrderItemDTO();
                    BeanUtils.copyProperties(x, allotOrderItemDTO);
                    return allotOrderItemDTO;
                }).toList();
        if (CollectionUtils.isEmpty(allotOrderItems)) {
            return;
        }
        // 作废
        AllotOrderDTO allotOrder = new AllotOrderDTO();
        BeanUtils.copyProperties(dbAllotOrder, allotOrder);
        allotOrder.setUserId(allotOrderDTO.getUserId());
        allotOrder.setAllotOrderItemDTOList(allotOrderItems);
        this.nullify(allotOrder);
    }

    @Override
    public void nullifyOrderByWarehouseId(Long warehouseId) {
        // 查找未完成/作废的调拨订单
        AllotOrderDTO allotOrderDTO = new AllotOrderDTO();
        allotOrderDTO.setWarehouseId(warehouseId);
        allotOrderDTO.setStatuses(Arrays.asList(AllotOrderStatusEnum.WAIT_STOCK.getNum(), AllotOrderStatusEnum.PARTIALLY_STOCK.getNum()));
        List<AllotOrderVO> allotOrders = allotOrderMapper.listAllotOrder(allotOrderDTO);
        if (CollectionUtils.isEmpty(allotOrders)) {
            return;
        }
        // 循环作废
        for (AllotOrderVO allotOrderVO : allotOrders) {
            List<AllotOrderItemDTO> allotOrderItems = allotOrderVO.getAllotOrderItemVOList().stream()
                    .filter(x -> x.getAllotCount() > x.getAllotInCount())
                    .map(x -> {
                        AllotOrderItemDTO allotOrderItemDTO = new AllotOrderItemDTO();
                        BeanUtils.copyProperties(x, allotOrderItemDTO);
                        return allotOrderItemDTO;
                    }).toList();
            // 作废
            AllotOrderDTO allotOrder = new AllotOrderDTO();
            BeanUtils.copyProperties(allotOrderVO, allotOrder);
            allotOrder.setAllotOrderItemDTOList(allotOrderItems);
            this.nullify(allotOrder);
        }
    }

    @Override
    public void downloadModel(HttpServletResponse response) {
        List<String> headers = Arrays.asList(
                I18nMessage.getMessage("yami.allot.excel.header.prodCode"),
                I18nMessage.getMessage("yami.allot.excel.header.prodName"),
                I18nMessage.getMessage("yami.allot.excel.header.allotCount")
        );
        String title = I18nMessage.getMessage("yami.allot.excel.title");
        // 生成excel
        ExcelWriter writer = ExcelUtil.getBigWriter();
        writer.merge(headers.size()-1, title);
        writer.writeRow(headers);
        Sheet sheet = writer.getSheet();
        for (int i=0; i<headers.size(); i++) {
            sheet.setColumnWidth(i, 20 * 256);
        }
        PoiExcelUtil.writeExcel(response, writer);
    }

    @Override
    public AllotSpuImportVO parseFile(MultipartFile excelFile, AllotOrderDTO allotOrderDTO) {
        // 参数检查
        this.checkWarehouse(allotOrderDTO.getOutWarehouseId(), allotOrderDTO.getShopId(), allotOrderDTO.getSysType());
        this.checkWarehouse(allotOrderDTO.getInWarehouseId(), allotOrderDTO.getShopId(), allotOrderDTO.getSysType());
        // 读取excel文件数据
        Map<String, Integer> allotMap = new HashMap<>(Constant.INITIAL_CAPACITY);
        List<String> errorList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        int allRow;
        try (InputStream inputStream = excelFile.getInputStream()){
            Workbook workbook = WorkbookFactory.create(inputStream);
            Sheet sheet = workbook.getSheetAt(0);
            allRow = sheet.getPhysicalNumberOfRows();
            //无内容
            if (allRow <= Constant.EXCEL_BEGIN_ROW) {
                throw new YamiShopBindException("yami.document.no.content");
            }
            Iterator<Row> rowIterator = sheet.rowIterator();
            // 过滤掉表头数据
            for (int i=0 ; i<Constant.EXCEL_BEGIN_ROW ; i++) {
                rowIterator.next();
            }
            // 读取表内容
            while (rowIterator.hasNext()) {
                Row row = rowIterator.next();
                try {
                    String partyCode = PoiExcelUtil.getRowValue(row, 0, false);
                    String inboundCount = PoiExcelUtil.getRowValue(row, 2, false);
                    allotMap.put(partyCode, Objects.isNull(inboundCount) ? 0 : Integer.parseInt(inboundCount));
                } catch (NumberFormatException e) {
                    errorList.add(String.format(
                            I18nMessage.getMessage("yami.allot.excel.inboud.must.int"),
                            row.getRowNum()));
                }
            }
        } catch (Exception e) {
            AllotSpuImportVO importVO = new AllotSpuImportVO();
            importVO.setTips(e.getMessage());
            return importVO;
        }
        // 查询sku数据
        SkuDto skuDto = new SkuDto();
        skuDto.setPartyCodes(new ArrayList<>(allotMap.keySet()));
        List<SkuVO> skuVOList = skuService.listSkuWithLang(skuDto);
        Set<Long> spuIds = new HashSet<>(Constant.INITIAL_CAPACITY);
        List<Long> skuIds = new ArrayList<>(Constant.INITIAL_CAPACITY);
        Map<Long, SkuVO> skuMap = new HashMap<>(Constant.INITIAL_CAPACITY);
        for (SkuVO skuVO : skuVOList) {
            spuIds.add(skuVO.getProdId());
            skuIds.add(skuVO.getSkuId());
            skuMap.put(skuVO.getSkuId(), skuVO);
        }
        // 检查sku
        StockPointSkuDTO stockPointSkuDTO = new StockPointSkuDTO();
        stockPointSkuDTO.setSkuIds(skuIds);
        stockPointSkuDTO.setStockPointIds(Collections.singletonList(allotOrderDTO.getOutWarehouseId()));
        List<StockPointSkuVO> stockPointSkus = stockPointSkuService.listStockPointSku(stockPointSkuDTO);
        if (CollectionUtils.isEmpty(stockPointSkus)) {
            throw new YamiShopBindException("yami.allot.item.error");
        }
        // 查询spu数据
        ProductDto productDto = new ProductDto();
        productDto.setProdIds(new ArrayList<>(spuIds));
        List<ProductVO> spuVOList = productService.listProdWithLang(productDto);
        Map<Long, ProductVO> spuMap = new HashMap<>(Constant.INITIAL_CAPACITY);
        if (!CollectionUtils.isEmpty(spuVOList)) {
            spuMap = spuVOList.stream().collect(Collectors.toMap(ProductVO::getProdId, x -> x));
        }
        // 查询sku对应调出/入仓库存
        List<SkuStockVO> skuStockVOList = new ArrayList<>(skuIds.size() * 2);
        for (Long skuId : skuIds) {
            SkuStockVO outSkuStockVO = new SkuStockVO(skuId);
            outSkuStockVO.setStockPointId(allotOrderDTO.getOutWarehouseId());
            skuStockVOList.add(outSkuStockVO);
            SkuStockVO inSkuStockVO = new SkuStockVO(skuId);
            inSkuStockVO.setStockPointId(allotOrderDTO.getInWarehouseId());
            skuStockVOList.add(inSkuStockVO);
        }
        Map<String, SkuStockVO> skuStockMap = skuStockService.listSkuStock(skuStockVOList);
        // 补充商品信息
        List<AllotSpuVO> allotSpuVOList = new ArrayList<>(allotMap.size());
        AllotSpuVO allotSpuVO;
        for (StockPointSkuVO stockPointSku : stockPointSkus) {
            SkuVO skuVO = skuMap.get(stockPointSku.getSkuId());
            allotSpuVO = this.buildAllotSpuVO(allotMap.get(skuVO.getPartyCode()), spuMap.get(skuVO.getProdId()), skuVO, skuStockMap, stockPointSku.getStockPointId(), allotOrderDTO.getInWarehouseId());
            if (Objects.isNull(allotSpuVO)) {
                continue;
            }
            allotSpuVOList.add(allotSpuVO);
        }
        // 构建返回信息
        AllotSpuImportVO allotSpuImportVO = new AllotSpuImportVO();
        allotSpuImportVO.setAllotSpuVOList(allotSpuVOList);
        allotSpuImportVO.setErrorList(errorList);
        allotSpuImportVO.setTips(String.format(
                I18nMessage.getMessage("yami.allot.excel.inboud.total.info"),
                allotMap.size(), allotSpuVOList.size(), allotMap.size()-allotSpuVOList.size()));
        return allotSpuImportVO;
    }

    @Override
    public void downloadInboundModel(HttpServletResponse response, AllotOrderDTO allotOrderDTO) {
        // 获取订单导入项数据
        AllotOrderVO allotOrder = allotOrderMapper.getAllotOrder(allotOrderDTO);
        if (Objects.isNull(allotOrder)) {
            throw new YamiShopBindException("yami.allot.order.null");
        }
        // 补充商品信息
        this.fillAllotItemInfo(allotOrder.getAllotOrderItemVOList());
        // 写入表头
        boolean isCn = Objects.equals(I18nMessage.getLang(), LanguageEnum.LANGUAGE_ZH_CN.getLang());
        List<String> headers = Arrays.asList(
                I18nMessage.getMessage("yami.allot.excel.inbound.header.seq"),
                I18nMessage.getMessage("yami.allot.excel.inbound.header.prodCode"),
                I18nMessage.getMessage("yami.allot.excel.inbound.header.prodName"),
                I18nMessage.getMessage("yami.allot.excel.inbound.header.spec"),
                I18nMessage.getMessage("yami.allot.excel.inbound.header.allotCount"),
                I18nMessage.getMessage("yami.allot.excel.inbound.header.canInboundCount"),
                I18nMessage.getMessage("yami.allot.excel.inbound.header.actualInboundCount")
        );
        String title = I18nMessage.getMessage("yami.allot.excel.inbound.title");
        ExcelWriter writer = ExcelUtil.getBigWriter();
        writer.merge(headers.size()-1, title);
        writer.writeRow(headers);
        Sheet sheet = writer.getSheet();
        for (int i=0; i<headers.size(); i++) {
            sheet.setColumnWidth(i, 20 * 256);
        }
        // 写入数据
        int index = 1;
        String spuName;
        String skuName;
        for (AllotOrderItemVO allotOrderItemVO : allotOrder.getAllotOrderItemVOList()) {
            if (isCn) {
                spuName = allotOrderItemVO.getSpuLangVOList().get(0).getProdName();
                skuName = allotOrderItemVO.getSkuLangVOList().get(0).getSkuName();
            } else {
                spuName = allotOrderItemVO.getSpuLangVOList().size() > 1 ? allotOrderItemVO.getSpuLangVOList().get(1).getProdName() : allotOrderItemVO.getSpuLangVOList().get(0).getProdName();
                skuName = allotOrderItemVO.getSkuLangVOList().size() > 1 ? allotOrderItemVO.getSkuLangVOList().get(1).getSkuName() : allotOrderItemVO.getSkuLangVOList().get(0).getSkuName();
            }
            Object[] excelData = {
                    index++,
                    allotOrderItemVO.getPartyCode(),
                    spuName,
                    skuName,
                    allotOrderItemVO.getAllotCount(),
                    allotOrderItemVO.getAllotCount() - allotOrderItemVO.getAllotInCount(),
                    allotOrderItemVO.getAllotCount() - allotOrderItemVO.getAllotInCount()
            };
            int col = 0;
            for (Object data : excelData) {
                PoiExcelUtil.mergeIfNeed(writer, index, index, col, col++, data);
            }
        }
        // 生成excel
        PoiExcelUtil.writeExcel(response, writer);
    }

    @Override
    public AllotSpuInboundImportVO parseInboundFile(MultipartFile excelFile, AllotOrderDTO allotOrderDTO) {
        AllotOrderVO allotOrder = allotOrderMapper.getAllotOrder(allotOrderDTO);
        if (Objects.isNull(allotOrder)) {
            throw new YamiShopBindException("yami.allot.order.null");
        }
        boolean isCn = Objects.equals(I18nMessage.getLang(), LanguageEnum.LANGUAGE_ZH_CN.getLang());
        // 补充商品信息
        this.fillAllotItemInfo(allotOrder.getAllotOrderItemVOList());
        // 解析excel文件
        Map<String, Integer> inboundMap = new HashMap<>(Constant.INITIAL_CAPACITY);
        List<String> errorList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        int allRow;
        try (InputStream inputStream = excelFile.getInputStream()){
            Workbook workbook = WorkbookFactory.create(inputStream);
            Sheet sheet = workbook.getSheetAt(0);
            allRow = sheet.getPhysicalNumberOfRows();
            //无内容
            if (allRow <= Constant.EXCEL_BEGIN_ROW) {
                throw new YamiShopBindException("yami.document.no.content");
            }
            Iterator<Row> rowIterator = sheet.rowIterator();
            // 过滤掉表头数据
            for (int i=0 ; i<Constant.EXCEL_BEGIN_ROW ; i++) {
                rowIterator.next();
            }
            // 读取表内容
            while (rowIterator.hasNext()) {
                Row row = rowIterator.next();
                try {
                    String partyCode = PoiExcelUtil.getRowValue(row, 1, false);
                    String inboundCount = PoiExcelUtil.getRowValue(row, 6, false);
                    inboundMap.put(partyCode, Objects.isNull(inboundCount) ? 0 : Integer.parseInt(inboundCount));
                } catch (NumberFormatException e) {
                    errorList.add(String.format(
                            isCn ? "第%d行数据错误, 实际入库数量需为整数" : "line %d is wrong, inbound count must be integer",
                            row.getRowNum()));
                }
            }
        } catch (Exception e) {
            AllotSpuInboundImportVO importVO = new AllotSpuInboundImportVO();
            importVO.setTips(e.getMessage());
            return importVO;
        }
        // 补充入库数量
        int successCount = 0;
        Integer inboundCount;
        for (AllotOrderItemVO allotOrderItemVO : allotOrder.getAllotOrderItemVOList()) {
            inboundCount = inboundMap.get(allotOrderItemVO.getPartyCode());
            if (Objects.isNull(inboundCount)) {
                allotOrderItemVO.setInboundCount(0);
                continue;
            }
            if (inboundCount < 0) {
                inboundCount = 0;
            }
            if (inboundCount > allotOrderItemVO.getAllotCount() - allotOrderItemVO.getAllotInCount()) {
                inboundCount = allotOrderItemVO.getAllotCount() - allotOrderItemVO.getAllotInCount();
            }
            allotOrderItemVO.setInboundCount(inboundCount);
            successCount ++;
        }
        // 构建返回信息
        AllotSpuInboundImportVO importVO = new AllotSpuInboundImportVO();
        importVO.setAllotOrderItemVOList(allotOrder.getAllotOrderItemVOList());

        importVO.setTips(String.format(
                isCn ? "检查到%d条商品信息，成功导入%d条商品信息！错误%d条商品信息!" : "total %d, success import %d, error data %d",
                allRow-2, successCount, allRow-2-successCount));
        importVO.setErrorList(errorList);
        return importVO;
    }

    @Override
    public Long countUnFinish(Long warehouseId) {
        if (Objects.isNull(warehouseId)) {
            return 0L;
        }
        return this.count(new LambdaQueryWrapper<AllotOrder>()
                .notIn(AllotOrder::getStatus, AllotOrderStatusEnum.VOIDED.getNum(), AllotOrderStatusEnum.COMPLETION.getNum()));
    }

    /**
     * 校验参数
     * @param allotOrderDTO 调拨订单dto
     */
    private void checkParam(AllotOrderDTO allotOrderDTO) {
        // 校验库存点
        WarehouseVO outWarehouse = this.checkWarehouse(allotOrderDTO.getOutWarehouseId(), allotOrderDTO.getShopId(), allotOrderDTO.getSysType());
        allotOrderDTO.setOutStockPointType(this.getStockPointType(outWarehouse.getSysType()));
        WarehouseVO inWarehouse = this.checkWarehouse(allotOrderDTO.getInWarehouseId(), allotOrderDTO.getShopId(), allotOrderDTO.getSysType());
        allotOrderDTO.setInStockPointType(this.getStockPointType(inWarehouse.getSysType()));
        // 校验商品库存
        List<AllotOrderItemDTO> allotOrderItemDTOList = allotOrderDTO.getAllotOrderItemDTOList();
        if (CollectionUtils.isEmpty(allotOrderItemDTOList)) {
            throw new YamiShopBindException("yami.allot.item.null");
        }
        List<Long> skuIds = new ArrayList<>(allotOrderItemDTOList.size());
        for (AllotOrderItemDTO allotOrderItemDTO : allotOrderItemDTOList) {
            if (Objects.isNull(allotOrderItemDTO.getAllotCount())
                    || Objects.equals(allotOrderItemDTO.getAllotCount(), 0)) {
                throw new YamiShopBindException("yami.allot.item.count.null");
            }
            skuIds.add(allotOrderItemDTO.getSkuId());
        }
        List<Long> containSkuIds = skuStockService.checkStockPointContainSku(Collections.singletonList(allotOrderDTO.getOutWarehouseId()), skuIds);
        if (!Objects.equals(skuIds.size(), containSkuIds.size())) {
            throw new YamiShopBindException("yami.allot.item.error");
        }
    }

    /**
     * 检查仓库
     * @param warehouseId 仓库id
     * @param shopId 店铺id
     * @param sysType 系统类型
     */
    private WarehouseVO checkWarehouse(Long warehouseId, Long shopId, Integer sysType) {
        WarehouseVO warehouse = warehouseService.getByWarehouseId(warehouseId);
        if (Objects.isNull(warehouse)) {
            throw new YamiShopBindException(String.format(I18nMessage.getMessage("yami.warehouse.null"), warehouseId));
        }
        if (Objects.equals(warehouse.getSysType(), SysTypeEnum.STATION.value())) {
            // 检查门店仓库
            Station station = stationMapper.getStationInfoById(warehouse.getShopId());
            if (!Objects.equals(station.getShopId(), shopId))  {
                throw new YamiShopBindException(String.format(I18nMessage.getMessage("yami.station.wrong"), station.getStationId()));
            }
        } else if (!Objects.equals(shopId, warehouse.getShopId())
                || !Objects.equals(sysType, warehouse.getSysType())) {
            // 检查商家仓库
            throw new YamiShopBindException(String.format(I18nMessage.getMessage("yami.warehouse.wrong"), warehouseId));
        }
        return warehouse;
    }

    /**
     * 构建调拨订单实体类
     * @param allotOrderDTO 调拨订单dto
     * @return 调拨订单
     */
    private AllotOrder buildAllotOrder(AllotOrderDTO allotOrderDTO) {
        AllotOrder allotOrder = new AllotOrder();
        BeanUtils.copyProperties(allotOrderDTO, allotOrder);
        allotOrder.setAllotOrderId(segmentService.getDateFormatSegmentId(SegmentIdKey.ALLOT_ORDER));
        allotOrder.setStatus(AllotOrderStatusEnum.WAIT_STOCK.getNum());
        allotOrder.setTotalAllotCount(allotOrderDTO.getAllotOrderItemDTOList().stream().mapToInt(AllotOrderItemDTO::getAllotCount).sum());
        allotOrder.setCreateTime(LocalDateTime.now());
        allotOrder.setUpdateTime(LocalDateTime.now());
        return allotOrder;
    }

    /**
     * 检查订单状态
     * @param allotOrderDTO 调拨订单dto
     */
    private AllotOrderVO checkOrderStatus(AllotOrderDTO allotOrderDTO) {
        AllotOrderVO dbAllotOrder = allotOrderMapper.getAllotOrder(allotOrderDTO);
        if (Objects.isNull(dbAllotOrder)) {
            throw new YamiShopBindException("yami.allot.order.null");
        }
        if (Objects.equals(dbAllotOrder.getStatus(), AllotOrderStatusEnum.VOIDED.getNum())) {
            throw new YamiShopBindException("yami.allot.order.nullify");
        }
        if (Objects.equals(dbAllotOrder.getStatus(), AllotOrderStatusEnum.COMPLETION.getNum())) {
            throw new YamiShopBindException("yami.allot.order.finish");
        }
        return dbAllotOrder;
    }

    /**
     * 获取库存点类型
     * @param warehouseSysType 库存点系统类型
     * @return 库存点类型
     */
    private Integer getStockPointType(Integer warehouseSysType) {
        // 系统类型为门店，库存点则为门店，其余都是仓库
        return Objects.equals(warehouseSysType, SysTypeEnum.STATION.value()) ? StockPointType.STATION.getValue() : StockPointType.WAREHOUSE.getValue();
    }

    /**
     * 补充调拨商品信息
     * @param allotOrderItemVOList 调拨商品集合
     */
    private void fillAllotItemInfo(List<AllotOrderItemVO> allotOrderItemVOList) {
        if (CollectionUtils.isEmpty(allotOrderItemVOList)) {
            return;
        }
        // 查询spu和sku
        Set<Long> spuIds = new HashSet<>(Constant.INITIAL_CAPACITY);
        List<Long> skuIds = new ArrayList<>(Constant.INITIAL_CAPACITY);
        for (AllotOrderItemVO allotOrderItemVO : allotOrderItemVOList) {
            spuIds.add(allotOrderItemVO.getSpuId());
            skuIds.add(allotOrderItemVO.getSkuId());
        }
        ProductDto productDto = new ProductDto();
        productDto.setProdIds(new ArrayList<>(spuIds));
        List<ProductVO> products = productService.listProdWithLang(productDto);
        Map<Long, ProductVO> spuMap = products.stream().collect(Collectors.toMap(ProductVO::getProdId, x -> x));
        SkuDto skuDto = new SkuDto();
        skuDto.setSkuIds(skuIds);
        List<SkuVO> skus = skuService.listSkuWithLang(skuDto);
        Map<Long, SkuVO> skuMap = skus.stream().collect(Collectors.toMap(SkuVO::getSkuId, x -> x));
        // 补充spu和sku名称
        for (AllotOrderItemVO allotOrderItemVO : allotOrderItemVOList) {
            ProductVO spuVO = spuMap.get(allotOrderItemVO.getSpuId());
            if (!Objects.isNull(spuVO)) {
                allotOrderItemVO.setSpuLangVOList(spuVO.getProdLangVOList());
                allotOrderItemVO.setMainImgUrl(spuVO.getPic());
            }
            SkuVO skuVO = skuMap.get(allotOrderItemVO.getSkuId());
            if (!Objects.isNull(skuVO)) {
                allotOrderItemVO.setPartyCode(skuVO.getPartyCode());
                allotOrderItemVO.setSkuLangVOList(skuVO.getSkuLangVOList());
                allotOrderItemVO.setImgUrl(skuVO.getPic());
            }
        }
    }

    /**
     * 构建sku库存VO
     * @param allotOrderItemDTO 调拨订单项
     * @param warehouseId 仓库id
     * @param stockPointType 仓库类型
     * @param luaOperateEnum lua库存脚本操作
     * @return sku库存VO
     */
    private SkuStockVO buildSkuStockVO(AllotOrderItemDTO allotOrderItemDTO,
                                       Long warehouseId,
                                       Integer stockPointType,
                                       LuaOperateEnum luaOperateEnum) {
        SkuStockVO skuStockVO = new SkuStockVO();
        BeanUtils.copyProperties(allotOrderItemDTO, skuStockVO);
        skuStockVO.setStockPointId(warehouseId);
        skuStockVO.setProdId(allotOrderItemDTO.getSpuId());
        skuStockVO.setStock(allotOrderItemDTO.getInboundCount());
        skuStockVO.setOperateType(luaOperateEnum.value());
        skuStockVO.setStockPointType(stockPointType);
        return skuStockVO;
    }

    /**
     * 构建库存流水VO集合
     * @param allotOrderDTO 调拨订单dto
     * @param warehouseId 仓库id
     * @param stockPointType 库存点类型
     * @param stockBillType 库存流水类型
     * @return 库存流水VO集合
     */
    private StockBillLog buildStockBillLog(AllotOrderDTO allotOrderDTO,
                                           Long warehouseId,
                                           Integer stockPointType,
                                           StockBillType stockBillType) {
        // 商品数据准备
        Set<Long> spuIds = new HashSet<>(Constant.INITIAL_CAPACITY);
        Set<Long> skuIds = new HashSet<>(Constant.INITIAL_CAPACITY);
        for (AllotOrderItemDTO allotOrderItemDTO : allotOrderDTO.getAllotOrderItemDTOList()) {
            spuIds.add(allotOrderItemDTO.getSpuId());
            skuIds.add(allotOrderItemDTO.getSkuId());
        }
        ProductDto productDto = new ProductDto();
        productDto.setProdIds(new ArrayList<>(spuIds));
        List<ProductVO> spuVOList = productService.listProdWithLang(productDto);
        Map<Long, ProductVO> spuMap = spuVOList.stream().collect(Collectors.toMap(ProductVO::getProdId, x -> x));
        SkuDto skuDto = new SkuDto();
        skuDto.setSkuIds(new ArrayList<>(skuIds));
        List<SkuVO> skuVOList = skuService.listSkuWithLang(skuDto);
        Map<Long, SkuVO> skuMap = skuVOList.stream().collect(Collectors.toMap(SkuVO::getSkuId, x -> x));
        // 获取库存入库单号
        boolean isOut = Objects.equals(stockBillType, StockBillType.ALLOT_OUT);
        StockBillLog stockBillLog = new StockBillLog();
        stockBillLog.setSourceOrderNo(allotOrderDTO.getAllotOrderId().toString());
        stockBillLog.setStockBillType(stockBillType.value());
        stockBillLog.setBusinessTime(new Date());
        stockBillLog.setRemark(allotOrderDTO.getRemark());
        if (Objects.equals(allotOrderDTO.getSysType(), SysTypeEnum.MULTISHOP.value())) {
            stockBillLog.setShopId(allotOrderDTO.getShopId());
        } else {
            stockBillLog.setSupplierId(allotOrderDTO.getShopId());
        }
        stockBillLog.setEmployeeId(ObjectUtils.isEmpty(allotOrderDTO.getUserId()) ? null : Long.parseLong(allotOrderDTO.getUserId()));
        stockBillLog.setType(isOut ? StockType.OUT_OF_STOCK.value() : StockType.WAREHOUSING.value());
        stockBillLog.setStatus(StockBillStatus.SUCCESS.value());
        stockBillLog.setStockPointId(warehouseId);
        stockBillLog.setStockPointType(stockPointType);
        double totalAmount = 0L;
        int totalCount = 0;
        List<StockBillLogItem> stockBillLogItems = new ArrayList<>(Constant.INITIAL_CAPACITY);
        StockBillLogItem stockBillLogItem;
        for (AllotOrderItemDTO allotOrderItemDTO : allotOrderDTO.getAllotOrderItemDTOList()) {
            stockBillLogItem = this.buildStockBillLogItem(allotOrderItemDTO, spuMap, skuMap);
            stockBillLogItems.add(stockBillLogItem);
            totalCount += stockBillLogItem.getStockCount();
            totalAmount += stockBillLogItem.getStockCount() * stockBillLogItem.getUnitPrice();
        }
        stockBillLog.setTotalAmount(totalAmount);
        stockBillLog.setTotalCount(totalCount);
        stockBillLog.setStockBillLogItems(stockBillLogItems);
        return stockBillLog;
    }

    /**
     * 构建库存流水项VO
     * @param allotOrderItemDTO 调拨订单dto
     * @param spuMap 商品spuMap
     * @param skuMap 商品skuMap
     * @return 库存流水项VO
     */
    private StockBillLogItem buildStockBillLogItem(AllotOrderItemDTO allotOrderItemDTO,
                                                     Map<Long, ProductVO> spuMap,
                                                     Map<Long, SkuVO> skuMap) {
        SkuVO skuVO = skuMap.get(allotOrderItemDTO.getSkuId());
        ProductVO spuVO = spuMap.get(allotOrderItemDTO.getSpuId());
        StockBillLogItem stockBillLogItem = new StockBillLogItem();
        stockBillLogItem.setStockCount(allotOrderItemDTO.getInboundCount());
        stockBillLogItem.setProdId(spuVO.getProdId());
        stockBillLogItem.setSkuId(skuVO.getSkuId());
        stockBillLogItem.setProdName(spuVO.getProdName());
        stockBillLogItem.setSkuName(skuVO.getSkuName());
        stockBillLogItem.setPartyCode(skuVO.getPartyCode());
        stockBillLogItem.setUnitPrice(skuVO.getPrice());
        return stockBillLogItem;
    }

    /**
     * 构建调拨商品数据
     * @param allotCount 调拨数量
     * @param spuVO 商品spuVO
     * @param skuVO 商品skuVO
     * @param skuStockMap 商品库存映射
     * @param outWarehouseId 出库仓id
     * @param inWarehouseId 入库仓id
     * @return 调拨商品
     */
    private AllotSpuVO buildAllotSpuVO(Integer allotCount,
                                       ProductVO spuVO,
                                       SkuVO skuVO,
                                       Map<String, SkuStockVO> skuStockMap,
                                       Long outWarehouseId,
                                       Long inWarehouseId) {
        if (Objects.isNull(spuVO)
                || Objects.isNull(skuVO)
                || Objects.isNull(allotCount)) {
            return null;
        }
        AllotSpuVO allotSpuVO = new AllotSpuVO();
        allotSpuVO.setPartyCode(skuVO.getPartyCode());
        allotSpuVO.setAllotCount(allotCount);
        allotSpuVO.setSkuId(skuVO.getSkuId());
        allotSpuVO.setImgUrl(skuVO.getPic());
        allotSpuVO.setSkuLangVOList(skuVO.getSkuLangVOList());
        allotSpuVO.setSpuId(spuVO.getProdId());
        allotSpuVO.setMainImgUrl(spuVO.getPic());
        allotSpuVO.setSpuLangVOList(spuVO.getProdLangVOList());
        SkuStockVO skuStockVO;
        allotSpuVO.setOutWarehouseStock(Objects.isNull((skuStockVO = skuStockMap.get(allotSpuVO.getSkuId() + "_" + outWarehouseId))) ? 0 : skuStockVO.getStock());
        allotSpuVO.setInWarehouseStock(Objects.isNull((skuStockVO = skuStockMap.get(allotSpuVO.getSkuId() + "_" + inWarehouseId))) ? 0 : skuStockVO.getStock());
        // 更新调拨数量（负数改为0，超出库存改为当前库存）
        if (allotSpuVO.getAllotCount() < 0) {
            allotSpuVO.setAllotCount(0);
        } else if (allotSpuVO.getAllotCount() > allotSpuVO.getOutWarehouseStock()) {
            allotSpuVO.setAllotCount(allotSpuVO.getOutWarehouseStock());
        }
        return allotSpuVO;
    }

    /**
     * 库存锁定
     * @param skuStockLocks sku库存锁定类
     */
    private void lockStock(List<SkuStockVO> skuStockLocks) {
        String result = skuStockLockService.lockAllotOrder(skuStockLocks);
        if (result.contains(Constant.UNDERLINE)) {
            return;
        }
        // 提示商品库存不足
        long skuId = Long.parseLong(result);
        Sku sku = skuService.getSkuAndName(skuId);
        throw new YamiShopBindException(String.format(I18nMessage.getMessage("yami.sku.stock.not.enough"), sku.getSkuName()));
    }

    /**
     * 作废操作
     * @param allotOrderDTO 调拨订单dto
     */
    private void nullify(AllotOrderDTO allotOrderDTO) {
        if (!CollectionUtils.isEmpty(allotOrderDTO.getAllotOrderItemDTOList())) {
            // 退还库存
            List<SkuStockVO> skuStockVOList = new ArrayList<>(Constant.INITIAL_CAPACITY);
            for (AllotOrderItemDTO allotOrderItemDTO : allotOrderDTO.getAllotOrderItemDTOList()) {
                allotOrderItemDTO.setInboundCount(allotOrderItemDTO.getAllotCount() - allotOrderItemDTO.getAllotInCount());
                skuStockVOList.add(this.buildSkuStockVO(allotOrderItemDTO, allotOrderDTO.getOutWarehouseId(), allotOrderDTO.getInStockPointType(), LuaOperateEnum.SKU_ADD));
            }
            skuStockService.changeSkuStock(skuStockVOList);
            // 新增入库库存流水
            StockBillLog stockBillLog = this.buildStockBillLog(allotOrderDTO, allotOrderDTO.getOutWarehouseId(), allotOrderDTO.getOutStockPointType(), StockBillType.ORDER_CANCEL);
            stockBillLogService.addStockBillLog(stockBillLog);
        }
        // 更新订单状态
        allotOrderDTO.setStatus(AllotOrderStatusEnum.VOIDED.getNum());
        this.updateAllotOrder(allotOrderDTO);
    }

    /**
     * 更新库存点和商品关系（删除库存为0的关联数据，库存减少后调用）
     * @param stockPointIds 库存点id集合
     * @param skuIds 商品skuId集合
     */
    void updateStockPointSkuRelate(List<Long> stockPointIds, List<Long> skuIds) {
        // 查询关联数据
        StockPointSkuDTO stockPointSkuDTO = new StockPointSkuDTO();
        stockPointSkuDTO.setStockPointIds(stockPointIds);
        stockPointSkuDTO.setSkuIds(skuIds);
        List<StockPointSkuVO> stockPointSkus = stockPointSkuService.listStockPointSku(stockPointSkuDTO);
        if (CollectionUtils.isEmpty(stockPointSkus)) {
            return;
        }
        // 查询库存
        List<SkuStockVO> skuStocks = new ArrayList<>(stockPointSkus.size());
        for (StockPointSkuVO stockPointSkuVO : stockPointSkus) {
            skuStocks.add(new SkuStockVO(stockPointSkuVO.getSkuId(), stockPointSkuVO.getStockPointId()));
        }
        Map<String, SkuStockVO> stockMap = skuStockService.listSkuStock(skuStocks);
        // 非默认仓库，且库存不存在 or 库存为0，删除关联数据
        List<Long> deleteData = new ArrayList<>(Constant.INITIAL_CAPACITY);
        for (StockPointSkuVO stockPointSkuVO : stockPointSkus) {
            if (Objects.equals(stockPointSkuVO.getType(), 0)) {
                continue;
            }
            SkuStockVO skuStockVO = stockMap.get(StockUtil.skuPointKey(stockPointSkuVO.getSkuId(), stockPointSkuVO.getStockPointId()));
            if (Objects.isNull(skuStockVO)
                    || Objects.equals(skuStockVO.getStock(), 0)) {
                deleteData.add(stockPointSkuVO.getStockPointSkuId());
            }
        }
        if (!CollectionUtils.isEmpty(deleteData)) {
            stockPointSkuService.deleteByIds(deleteData);
        }
    }
}
